<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        #box-wrap{
            margin: 50px 500px;
            position: relative;
        }
        .boxItem{
            background: skyblue;
            border: 1px solid #e7e7d1;
        }
        .barriersItem{
            background: #000000;
            border: 1px solid yellow;
        }
        .start{
            background:darkblue;
            border: 1px solid yellow;
        }
        .end{
            background: red;
            border: 1px solid yellow;
        }
        .path{
            background: darkgray;
            border: 1px solid yellow;
            transition: all 1s;
        }
        .btns-wrap{
            position: absolute;
            top: 700px;
            left: 500px;
        }
    </style>
</head>
<body>

<div id="box-wrap"></div>

<div class="btns-wrap">
    <button id="setBarriers">设置障碍物</button>
    <button id="setStart">设置起点</button>
    <button id="setEnd">设置终点</button>
    <button id="search">开始寻路</button>
    <button id="clear">清空</button>
</div>
<script>
    /*
    *说明：曼哈顿距离：横纵坐标得相减得绝对值
    * 设计思路：拿到开始点数据，放到open列表中，获取到该点得周围上下左右得数据，将开始点和周围点放在close列表中,计算距离，排序，拿到最小值，放到path路径中，open列表除了最小值，其他全部删除
   */
    const row = 30,//方格行数
        col = 30,//方格列数
        fangGeWidth = 20, fangGeHeight = 20;//展示图中方格得大小

    let open = [],close = [],startPoint = {row:0,col:0},endPoint = {row:30,col:12},path = [],map = [];
    const boxWrap = document.getElementById('box-wrap');
    function render() {
        const documentFragment = document.createDocumentFragment();
        for (let i = 0;i<row;i++){
            map[i] = [];
            for (let j = 0;j<col;j++){
                let tempDiv = document.createElement('div');
                tempDiv.row = i;
                tempDiv.col = j;
                tempDiv.style.width = fangGeWidth+'px';
                tempDiv.style.height = fangGeHeight+'px';
                tempDiv.style.position = 'absolute';
                tempDiv.style.left = fangGeWidth*j+'px';
                tempDiv.style.top = fangGeHeight*i+'px';
                documentFragment.appendChild(tempDiv);
                map[i][j] = {
                    row:i,
                    col:j,
                    div:tempDiv
                };
                tempDiv.className = 'boxItem';
            }
        }
        boxWrap.appendChild(documentFragment);
    }
    function drawTools(obj,className) {
        obj.div.className = className;
    }
    function checkBarriers(arr,row,col){
        return arr.some(item=>{
            return(item.row === row && item.col === col)
        })
    }
    function findAroundData(target){
        if(!target){return};
        let up = target.row-1,
            down = target.row+1,
            left = target.col - 1,
            right = target.col+1;
        // 上\
        if(!checkBarriers(close,up,target.col) && up>=0){
            open.push(map[up][target.col]);
        }
        //下
        if(!checkBarriers(close,down,target.col) && down<row){
            open.push(map[down][target.col]);
        }
        //左
        if(!checkBarriers(close,target.row,left) && left>=0){
            open.push(map[target.row][left]);
        }
        if(!checkBarriers(close,target.row,right) && right<col){
            open.push(map[target.row][right]);
        }
    }
    function manhattan(current,end) {
        if(current && end && current.row && end.row && current.col && end.col){
            return Math.abs(current.row - end.row)+Math.abs(current.col - end.col)
        }
    }
    function findWay(startPoint,endPoint){
        if(startPoint && endPoint && startPoint.row === endPoint.row && startPoint.col === endPoint.col){return;}
        findAroundData(startPoint);
        open = open.map(item=>{
            item.F = manhattan(item,endPoint);
            return item;
        });
        open.sort((a,b)=>{
            return a?.F-b?.F
        });

        const temp = open.splice(0,1);
        open = [];
        if(temp && endPoint.row && endPoint.col){
            close.push(...temp);
            if(checkBarriers(close,endPoint.row,endPoint.col)){ //如果有一个数据包含结尾数据，则打断
                return;
            }
            path.push(...temp);
            console.log(path)
            return findWay(...temp,endPoint);
        }
    }

    render();

    let currentStatus = 'setStart',
        setBarriers = document.getElementById('setBarriers'),
        setStart = document.getElementById('setStart'),
        setEnd = document.getElementById('setEnd'),
        search = document.getElementById('search'),
        clear = document.getElementById('clear');

    setBarriers.onclick = function () {
        currentStatus = 'setBarriers';
    };

    setStart.onclick = function () {
        currentStatus = 'setStart';
    };
    setEnd.onclick = function() {
        currentStatus = 'setEnd';
    };
    const config = {
        'setBarriers':function (target) {
            target.className='barriersItem';
            const row = target.row, col = target.col;
            close.push({
                row:row,
                col:col,
                div:target
            });
        },
        'setStart':function (target) {
            target.className='start';
            const row = target.row, col = target.col;
            startPoint = {
                row:row,
                col:col,
                div:target
            };
            close.push(startPoint);
        },
        'setEnd':function (target) {
            target.className='end';
            const row = target.row, col = target.col;
            endPoint = {
                row:row,
                col:col,
                div:target
            }
        },
    };
    search.onclick = function(){
        findWay(startPoint,endPoint);
        path.forEach((item,index)=>{
            setTimeout(()=>{
                drawTools(item,'path');
            },index*100);
        });
    };
    boxWrap.onclick = function (e) {
        config[currentStatus](e.target)
    };
    clear.onclick = function () {
        location.reload();
    };


</script>
</body>
</html>